﻿@page "/traces/detail/{traceId}"

@using iPanel.Model.Otlp
@using iPanel.Otlp.Model
@using System.Globalization
@using iPanel.Components.Controls.Grid
@using iPanel.Resources
@using iPanel.Utils
@inject IStringLocalizer<iPanel.Resources.TraceDetail> Loc
@inject IStringLocalizer<ControlsStrings> ControlStringsLoc

<PageTitle>
    <ApplicationName
        AdditionalText="@GetPageTitle()"
        ResourceName="@nameof(iPanel.Resources.TraceDetail.TraceDetailPageTitle)"
        Loc="@Loc"/>
</PageTitle>

<div class="page-content-container">
    @if (_trace is { } trace)
    {
        <AspirePageContentLayout
            AddNewlineOnToolbar="true"
            MobileToolbarButtonText="@Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailMobileToolbarButtonText)]"
            IsSummaryDetailsViewOpen="@(SelectedSpan is not null)">
            <PageTitleSection>
                <div class="page-header">
                    <h1>
                        <span>@GetPageTitle()</span>
                        <span class="trace-id">@OtlpHelpers.ToShortenedId(trace.TraceId)</span>
                    </h1>
                </div>
            </PageTitleSection>
            <ToolbarSection>
                <div class="toolbar-left">
                    @Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailTraceStartHeader)] <strong title="@FormatHelpers.FormatDateTime(TimeProvider, _trace.FirstSpan.StartTime, MillisecondsDisplay.Full)">@FormatHelpers.FormatDateTime(TimeProvider, _trace.FirstSpan.StartTime, MillisecondsDisplay.Truncated)</strong>
                </div>
                <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
                <div>
                    @Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailDurationHeader)] <strong>@DurationFormatter.FormatDuration(trace.Duration)</strong>
                </div>
                <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
                <div>
                    @Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailResourcesHeader)] <strong>@trace.Spans.GroupBy(s => s.Source).Count()</strong>
                </div>
                <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
                <div>
                    @Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailDepthHeader)] <strong>@_maxDepth</strong>
                </div>
                <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
                <div>
                    @Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailTotalSpansHeader)] <strong>@trace.Spans.Count</strong>
                </div>

                <FluentSearch @bind-Value="_filter"
                              @bind-Value:after="HandleAfterFilterBindAsync"
                              Immediate="true"
                              ImmediateDelay="@FluentUIExtensions.InputDelay"
                              Placeholder="@ControlStringsLoc[nameof(ControlsStrings.FilterPlaceholder)]"
                              title="@Loc[nameof(iPanel.Resources.Traces.TracesNameFilter)]"
                              slot="end" />
                <FluentDivider slot="end" Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
                <FluentAnchor slot="end" Appearance="Appearance.Lightweight" Href="@DashboardUrls.StructuredLogsUrl(traceId: trace.TraceId)">@ControlStringsLoc[nameof(ControlsStrings.ViewLogsLink)]</FluentAnchor>
            </ToolbarSection>
            <MainSection>
                <SummaryDetailsView
                    ShowDetails="SelectedSpan is not null"
                    OnDismiss="@(() => ClearSelectedSpanAsync(causedByUserAction: true))"
                    ViewKey="TraceDetail"
                    SelectedValue="@SelectedSpan"
                    OnResize="@(r => _manager.SetWidthFraction(r.Orientation == Orientation.Horizontal ? r.Panel1Fraction : 1))">
                    <DetailsTitleTemplate>
                        @{ var shortedSpanId = OtlpHelpers.ToShortenedId(context!.Span.SpanId); }
                        <div class="pane-details-title" title="@($"{context!.Title} ({shortedSpanId})")">
                            @context!.Title
                            <span class="pane-details-subtext">@shortedSpanId</span>
                        </div>
                    </DetailsTitleTemplate>
                    <Summary>
                        <GridColumnManager @ref="_manager" Columns="@_gridColumns">
                            <FluentDataGrid @ref="_dataGrid"
                                            Virtualize="true"
                                            GenerateHeader="GenerateHeaderOption.Sticky"
                                            Class="main-grid trace-view-grid enable-row-click"
                                            ResizableColumns="true"
                                            ItemsProvider="@GetData"
                                            TGridItem="SpanWaterfallViewModel"
                                            RowClass="@GetRowClass"
                                            GridTemplateColumns="@_manager.GetGridTemplateColumns()"
                                            RowSize="DataGridRowSize.Small"
                                            ShowHover="true"
                                            ItemKey="@(r => r.Span.SpanId)"
                                            OnRowClick="@(r => r.ExecuteOnDefault(d => OnShowPropertiesAsync(d, buttonId: null)))">
                                <AspireTemplateColumn ColumnId="@NameColumn" ColumnManager="@_manager" Title="@Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailNameHeader)]" Tooltip="true" TooltipText="@(c => c.GetTooltip(_applications))" Class="expand-col">
                                    @{
                                        var isServerOrConsumer = context.Span.Kind == OtlpSpanKind.Server || context.Span.Kind == OtlpSpanKind.Consumer;
                                        // Indent the span name based on the depth of the span.
                                        var marginLeft = (context.Depth - 1) * 15;

                                        // We want to have consistent margin for both client and server spans.
                                        string spanNameContainerStyle;
                                        if (!isServerOrConsumer)
                                            {
                                                // Client span has 19px extra content:
                                                // - 5px extra margin-left
                                                // - 5px border
                                                // - 9px padding-left
                                                spanNameContainerStyle = $"margin-left: 5px; border-left-color: {ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source))}; border-left-width: 5px; border-left-style: solid; padding-left: 9px;";
                                            }
                                        else
                                        {
                                            // Span with icon has 19px extra content:
                                            // - 16px icon
                                            // - 3px padding-left
                                            spanNameContainerStyle = string.Empty;
                                        }
                                    }

                                    <span class="span-overview-container" style="margin-left: @(marginLeft)px;">
                                        <span @onclick:stopPropagation="true" class="main-grid-expand-container @(context.IsCollapsed ? "main-grid-collapsed" : "main-grid-expanded")">
                                            @if (context.Children.Count > 0)
                                            {
                                                <FluentButton Appearance="Appearance.Lightweight" Class="main-grid-expand-button" OnClick="@(() => OnToggleCollapse(context))">
                                                    <FluentIcon Icon="Icons.Regular.Size12.ChevronRight" Color="Color.Neutral" />
                                                </FluentButton>
                                            }
                                        </span>
                                        <span class="span-name-container" style="@spanNameContainerStyle">
                                            @if (isServerOrConsumer)
                                            {
                                                <FluentIcon Class="span-kind-icon"
                                                            Color="Color.Custom"
                                                            CustomColor="@ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source))"
                                                            Value="@GetSpanIcon(context.Span)"/>
                                            }

                                            @if (context.IsError)
                                            {
                                                <FluentIcon Icon="Icons.Filled.Size12.ErrorCircle" Color="Color.Error" Class="trace-tag-icon"/>
                                            }
                                            <FluentHighlighter HighlightedText="@_filter" Text="@GetResourceName(context.Span.Source)" />
                                            @if (context.HasUninstrumentedPeer)
                                            {
                                                <span class="uninstrumented-peer">
                                                    @{
                                                        Icon icon;
                                                        if (context.Span.Attributes.HasKey("db.system"))
                                                        {
                                                            icon = new Icons.Filled.Size16.Database();
                                                        }
                                                        else if (context.Span.Attributes.HasKey("messaging.system"))
                                                        {
                                                            icon = new Icons.Filled.Size16.Mail();
                                                        }
                                                        else
                                                        {
                                                            // Everything else.
                                                            icon = new Icons.Filled.Size16.ArrowCircleRight();
                                                        }
                                                    }
                                                    <FluentIcon
                                                        Style="@($"fill: {ColorGenerator.Instance.GetColorHexByKey(context.UninstrumentedPeer)};")"
                                                        Value="icon"
                                                        Class="uninstrumented-peer-icon"/>
                                                    <FluentHighlighter HighlightedText="@_filter" Text="@context.UninstrumentedPeer" />
                                                </span>
                                            }
                                            <span class="span-row-name"><FluentHighlighter HighlightedText="@_filter" Text="@context.Span.GetDisplaySummary()" /></span>
                                        </span>
                                    </span>
                                </AspireTemplateColumn>
                                <AspireTemplateColumn ColumnId="@TicksColumn" ColumnManager="@_manager">
                                    <HeaderCellItemTemplate>
                                        <div class="ticks">
                                            @* First column starts at 0. We don't want to display the smallest unit (0μs) because that looks odd. Use the unit from the next column *@
                                            <div class="tick" style="grid-column: 1;"></div>
                                            <span class="tick-label" style="grid-column: 1;">@($"0{DurationFormatter.GetUnit(trace.Duration / 4)}")</span>

                                            <div class="tick" style="grid-column: 2;"></div>
                                            <span class="tick-label" style="grid-column: 2;">@DurationFormatter.FormatDuration(trace.Duration / 4)</span>

                                            <div class="tick" style="grid-column: 3;"></div>
                                            <span class="tick-label" style="grid-column: 3;">@DurationFormatter.FormatDuration(trace.Duration / 4 * 2)</span>

                                            @* Grid column 4 shows two labels. Hide the left aligned label on mobile to avoid them overlapping *@
                                            <div class="tick" style="grid-column: 4;"></div>
                                            @if (_manager.ViewportInformation.IsDesktop)
                                            {
                                                <span class="tick-label" style="grid-column: 4;">@DurationFormatter.FormatDuration(trace.Duration / 4 * 3)</span>
                                            }

                                            <span class="tick-label end-tick" style="grid-column: 4;">@DurationFormatter.FormatDuration(trace.Duration)</span>
                                            <div class="tick" style="grid-column: 5;"></div>
                                        </div>
                                    </HeaderCellItemTemplate>
                                    <ChildContent>
                                        <div class="ticks">
                                            <div class="span-container" style="grid-template-columns: @context.LeftOffset.ToString("F2", CultureInfo.InvariantCulture)% @context.Width.ToString("F2", CultureInfo.InvariantCulture)% min-content;">
                                                <div class="span-bar" style="grid-column: 2; background: @ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source));"></div>
                                                <div class="span-bar-label @(context.LabelIsRight ? "span-bar-label-right" : "span-bar-label-left")">
                                                    <span class="span-bar-label-detail">@SpanWaterfallViewModel.GetTitle(context.Span, _applications)</span>
                                                    <span>@DurationFormatter.FormatDuration(context.Span.Duration)</span>
                                                </div>
                                            </div>
                                            <div class="tick" style="grid-column: 1;"></div>
                                            <div class="tick" style="grid-column: 2;"></div>
                                            <div class="tick" style="grid-column: 3;"></div>
                                            <div class="tick" style="grid-column: 4;"></div>
                                            <div class="tick" style="grid-column: 5;"></div>
                                        </div>
                                    </ChildContent>
                                </AspireTemplateColumn>
                                <AspireTemplateColumn ColumnId="@ActionsColumn" ColumnManager="@_manager" Title="@ControlStringsLoc[nameof(ControlsStrings.ActionsColumnHeader)]" Class="no-ellipsis">
                                    @{
                                        var id = context.Span.SpanId;
                                    }

                                    <div @onclick:stopPropagation="true" style="margin-left: 7px;">
                                        <SpanActions SpanViewModel="@context"
                                                     OnViewDetails="@((buttonId) => OnShowPropertiesAsync(context, buttonId))" />
                                    </div>
                                </AspireTemplateColumn>
                            </FluentDataGrid>
                        </GridColumnManager>
                    </Summary>
                    <Details>
                        <SpanDetails ViewModel="context" />
                    </Details>
                </SummaryDetailsView>
            </MainSection>
        </AspirePageContentLayout>
    }
    else
    {
        <div class="empty-content">
            <FluentIcon Icon="Icons.Regular.Size24.GanttChart" /> &nbsp; @string.Format(Loc[nameof(iPanel.Resources.TraceDetail.TraceDetailTraceNotFound)], TraceId)
        </div>
    }
</div>
